public class Board {

    private final int NUM_OF_COLUMNS = 7;
    private final int NUM_OF_ROW = 6;

    // TODO char数组默认初始化字符，不顺眼可以改掉
    private final char INIT_CHAR = '\u0000';
    // TODO Connect *N*，不顺眼可以代码替换为4写成魔法数
    private final int WIN_COND = 4;

    /*
     * The board object must contain the board state in some manner.
     * You must decide how you will do this.
     *
     * You may add addition private/public methods to this class is you wish.
     * However, you should use best OO practices. That is, you should not expose
     * how the board is being implemented to other classes. Specifically, the
     * Player classes.
     *
     */

    private char[][] boardContain;

    // used to check game status easier
    private int[] lastMove = new int[2];

    public Board() {
        //TODO
    }

    public void getLastMove(int[] lastMove) {
        this.lastMove = lastMove;
    }

    public void printBoard() {
        for (int i = 0; i < NUM_OF_ROW; i++) {
            for (int j = 0; j < NUM_OF_COLUMNS; j++) {
                System.out.print("|");
                if (boardContain[j][i] == INIT_CHAR) {
                    if (i == NUM_OF_ROW - 1) {
                        System.out.print("_");
                    } else {
                        System.out.print(" ");
                    }
                } else {
                    System.out.print(boardContain[j][i]);
                }
            }
            System.out.println("|");
        }
    }

    public boolean containsWin() {
        boolean horizontal = false;
        boolean vertical = false;
        boolean diagonal = false;
        int cnt = 1;
        char tmpChar;
        // Only judge if the last move make game win
        char finalChar = boardContain[lastMove[0]][lastMove[1]] == INIT_CHAR ? '0' : boardContain[lastMove[0]][lastMove[1]];
        // TODO 写法有点蠢，单个方向可以只用一个for搞定
        // Horizontal
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] + i < NUM_OF_COLUMNS) {
                tmpChar = boardContain[lastMove[0] + i][lastMove[1]];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] - i >= 0) {
                tmpChar = boardContain[lastMove[0] - i][lastMove[1]];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        if (cnt >= WIN_COND) {
            horizontal = true;
        }
        // Vertical
        cnt = 1;
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[1] + i < NUM_OF_ROW) {
                tmpChar = boardContain[lastMove[0]][lastMove[1] + i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[1] - i >= 0) {
                tmpChar = boardContain[lastMove[0]][lastMove[1] - i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        if (cnt >= WIN_COND) {
            vertical = true;
        }
        // Diagonal ↗↙
        cnt = 1;
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] + i < NUM_OF_COLUMNS && lastMove[1] - i > 0) {
                tmpChar = boardContain[lastMove[0] + i][lastMove[1] - i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] - i >= 0 && lastMove[1] + i < NUM_OF_ROW) {
                tmpChar = boardContain[lastMove[0] - i][lastMove[1] + i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        if (cnt >= WIN_COND) {
            diagonal = true;
        }
        // Diagonal ↖↘
        cnt = 1;
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] - i >= 0 && lastMove[1] + i < NUM_OF_ROW) {
                tmpChar = boardContain[lastMove[0] - i][lastMove[1] + i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        for (int i = 1; i <= WIN_COND; i++) {
            if (lastMove[0] + i < NUM_OF_COLUMNS && lastMove[1] - i >= 0) {
                tmpChar = boardContain[lastMove[0] + i][lastMove[1] - i];
                if (tmpChar == finalChar) {
                    cnt++;
                } else {
                    break;
                }
            }
        }
        if (cnt >= WIN_COND) {
            diagonal = true;
        }
        // Summary
        return horizontal || vertical || diagonal;
    }

    //TODO 暴力法,验证棋盘是否已满。搞个算法提前平局
    public boolean isTie() {
        if (!containsWin()) {
            for (int i = 0; i < NUM_OF_ROW; i++) {
                for (int j = 0; j < NUM_OF_COLUMNS; j++) {
                    if (boardContain[j][i] == INIT_CHAR) {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }

    public void reset() {
        //TODO
        boardContain = new char[NUM_OF_COLUMNS][NUM_OF_ROW];
    }

    public boolean drawMove(char symbol, int playerMove) {
        int row = 0;

        if (!isDrawable(playerMove)) {
            return false;
        }
        for (int i = NUM_OF_ROW - 1; i >= 0; i--) {
            if (boardContain[playerMove][i] == INIT_CHAR) {
                row = i;
                break;
            }
        }
        boardContain[playerMove][row] = symbol;
        lastMove = new int[]{playerMove, row};
        return true;
    }

    private boolean isDrawable(int playerMove) {
        if (playerMove >= NUM_OF_COLUMNS || playerMove < 0) {
            return false;
        }
        return boardContain[playerMove][0] == INIT_CHAR;
    }

}
